home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / WWW / apache_1.0.5 / src / alloc.c next >
C/C++ Source or Header  |  1996-02-16  |  22KB  |  917 lines

  1.  
  2. /* ====================================================================
  3.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer. 
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright
  13.  *    notice, this list of conditions and the following disclaimer in
  14.  *    the documentation and/or other materials provided with the
  15.  *    distribution.
  16.  *
  17.  * 3. All advertising materials mentioning features or use of this
  18.  *    software must display the following acknowledgment:
  19.  *    "This product includes software developed by the Apache Group
  20.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  21.  *
  22.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  23.  *    endorse or promote products derived from this software without
  24.  *    prior written permission.
  25.  *
  26.  * 5. Redistributions of any form whatsoever must retain the following
  27.  *    acknowledgment:
  28.  *    "This product includes software developed by the Apache Group
  29.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  32.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  33.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  35.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  42.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  43.  * ====================================================================
  44.  *
  45.  * This software consists of voluntary contributions made by many
  46.  * individuals on behalf of the Apache Group and was originally based
  47.  * on public domain software written at the National Center for
  48.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  49.  * For more information on the Apache Group and the Apache HTTP server
  50.  * project, please see <http://www.apache.org/>.
  51.  *
  52.  */
  53.  
  54.  
  55. /*
  56.  * Resource allocation code... the code here is responsible for making
  57.  * sure that nothing leaks.
  58.  *
  59.  * rst --- 4/95 --- 6/95
  60.  */
  61.  
  62. #include "conf.h"
  63. #include "alloc.h"
  64.  
  65. #include <stdarg.h>
  66.  
  67. /*****************************************************************
  68.  *
  69.  * Managing free storage blocks...
  70.  */
  71.  
  72. union align
  73. {
  74.   /* Types which are likely to have the longest RELEVANT alignment
  75.    * restrictions... we don't do much with doubles.
  76.    */
  77.   
  78.   char *cp;
  79.   void (*f)();
  80.   long l;
  81.   FILE *fp;
  82. };
  83.  
  84. #define CLICK_SZ (sizeof(union align))
  85.  
  86. union block_hdr
  87. {
  88.   union align a;
  89.   
  90.   /* Actual header... */
  91.   
  92.   struct {
  93.     char *endp;
  94.     union block_hdr *next;
  95.     char *first_avail;
  96.   } h;
  97. };
  98.  
  99. union block_hdr *block_freelist = NULL;
  100.  
  101.  
  102.  
  103. /* Get a completely new block from the system pool */
  104.  
  105. union block_hdr *malloc_block (int size)
  106. {
  107.   union block_hdr *blok =
  108.     (union block_hdr *)malloc(size + sizeof(union block_hdr));
  109.  
  110.   if (blok == NULL) return NULL;
  111.   
  112.   blok->h.next = NULL;
  113.   blok->h.first_avail = (char *)(blok + 1);
  114.   blok->h.endp = size + blok->h.first_avail;
  115.   
  116.   return blok;
  117. }
  118.  
  119.  
  120.  
  121. void chk_on_blk_list (union block_hdr *blok, union block_hdr *free_blk)
  122. {
  123.   /* Debugging code.  Left in for the moment. */
  124.     
  125.   while (free_blk) {
  126.     if (free_blk == blok) {
  127.       fprintf (stderr, "Ouch!  Freeing free block\n");
  128.       exit (1);
  129.     }
  130.     free_blk = free_blk->h.next;
  131.   }
  132. }
  133.  
  134. /* Free a chain of blocks --- must be called with alarms blocked. */
  135.  
  136. void free_blocks (union block_hdr *blok)
  137. {
  138.   /* First, put new blocks at the head of the free list ---
  139.    * we'll eventually bash the 'next' pointer of the last block
  140.    * in the chain to point to the free blocks we already had.
  141.    */
  142.   
  143.   union block_hdr *old_free_list = block_freelist;
  144.  
  145.   if (blok == NULL) return;    /* Sanity check --- freeing empty pool? */
  146.   
  147.   block_freelist = blok;
  148.   
  149.   /*
  150.    * Next, adjust first_avail pointers of each block --- have to do it
  151.    * sooner or later, and it simplifies the search in new_block to do it
  152.    * now.
  153.    */
  154.  
  155.   while (blok->h.next != NULL) {
  156.     chk_on_blk_list (blok, old_free_list);
  157.     blok->h.first_avail = (char *)(blok + 1);
  158.     blok = blok->h.next;
  159.   }
  160.  
  161.   chk_on_blk_list (blok, old_free_list);
  162.   blok->h.first_avail = (char *)(blok + 1);
  163.  
  164.   /* Finally, reset next pointer to get the old free blocks back */
  165.  
  166.   blok->h.next = old_free_list;
  167. }
  168.  
  169.  
  170.  
  171.  
  172. /* Get a new block, from our own free list if possible, from the system
  173.  * if necessary.  Must be called with alarms blocked.
  174.  */
  175.  
  176. union block_hdr *new_block (int min_size)
  177. {
  178.   union block_hdr **lastptr = &block_freelist;
  179.   union block_hdr *blok = block_freelist;
  180.   
  181.   /* First, see if we have anything of the required size
  182.    * on the free list...
  183.    */
  184.  
  185.   min_size += BLOCK_MINFREE;
  186.  
  187.   while (blok != NULL) {
  188.     if (min_size <= blok->h.endp - blok->h.first_avail) {
  189.       
  190.       *lastptr = blok->h.next;
  191.       blok->h.next = NULL;
  192.       return blok;
  193.     }
  194.     else {
  195.       lastptr = &blok->h.next;
  196.       blok = blok->h.next;
  197.     }
  198.   }
  199.  
  200.   /* Nope. */
  201.  
  202.   return malloc_block (min_size);
  203. }
  204.  
  205.  
  206.  
  207. /* Accounting */
  208.  
  209. long bytes_in_block_list (union block_hdr *blok)
  210. {
  211.   long size = 0;
  212.  
  213.   while (blok) {
  214.     size += blok->h.endp - (char *)(blok + 1);
  215.     blok = blok->h.next;
  216.   }
  217.  
  218.   return size;
  219. }
  220.  
  221.  
  222. /*****************************************************************
  223.  *
  224.  * Pool internals and management...
  225.  * NB that subprocesses are not handled by the generic cleanup code,
  226.  * basically because we don't want cleanups for multiple subprocesses
  227.  * to result in multiple three-second pauses.
  228.  */
  229.  
  230. struct process_chain;
  231. struct cleanup;
  232.  
  233. static void run_cleanups (struct cleanup *);
  234. static void free_proc_chain (struct process_chain *);
  235.  
  236. struct pool {
  237.   union block_hdr *first;
  238.   union block_hdr *last;
  239.   struct cleanup *cleanups;
  240.   struct process_chain *subprocesses;
  241.   struct pool *sub_pools;
  242.   struct pool *sub_next;
  243.   struct pool *sub_prev;
  244.   struct pool *parent;
  245.   char *free_first_avail;
  246. };
  247.  
  248. pool *permanent_pool;
  249.  
  250. /* Each pool structure is allocated in the start of its own first block,
  251.  * so we need to know how many bytes that is (once properly aligned...).
  252.  * This also means that when a pool's sub-pool is destroyed, the storage
  253.  * associated with it is *completely* gone, so we have to make sure it
  254.  * gets taken off the parent's sub-pool list...
  255.  */
  256.  
  257. #define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ))
  258. #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)             
  259.  
  260. struct pool *make_sub_pool (struct pool *p)
  261. {
  262.   union block_hdr *blok;
  263.   pool *new_pool;
  264.  
  265.   block_alarms();
  266.   
  267.   blok = new_block (0);
  268.   new_pool = (pool *)blok->h.first_avail;
  269.   blok->h.first_avail += POOL_HDR_BYTES;
  270.  
  271.   memset ((char *)new_pool, '\0', sizeof (struct pool));
  272.   new_pool->free_first_avail = blok->h.first_avail;
  273.   new_pool->first = new_pool->last = blok;
  274.     
  275.   if (p) {
  276.     new_pool->parent = p;
  277.     new_pool->sub_next = p->sub_pools;
  278.     if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool;
  279.     p->sub_pools = new_pool;
  280.   }
  281.   
  282.   unblock_alarms();
  283.   
  284.   return new_pool;
  285. }
  286.  
  287. void init_alloc() { permanent_pool = make_sub_pool (NULL); }
  288.  
  289. void clear_pool (struct pool *a)
  290. {
  291.   block_alarms();
  292.   
  293.   while (a->sub_pools)
  294.     destroy_pool (a->sub_pools);
  295.     
  296.   a->sub_pools = NULL;
  297.   
  298.   run_cleanups (a->cleanups);        a->cleanups = NULL;
  299.   free_proc_chain (a->subprocesses); a->subprocesses = NULL;
  300.   free_blocks (a->first->h.next);    a->first->h.next = NULL;
  301.  
  302.   a->last = a->first;
  303.   a->first->h.first_avail = a->free_first_avail;
  304.  
  305.   unblock_alarms();
  306. }
  307.  
  308. void destroy_pool (pool *a)
  309. {
  310.   block_alarms();
  311.   clear_pool (a);
  312.  
  313.   if (a->parent) {
  314.     if (a->parent->sub_pools == a) a->parent->sub_pools = a->sub_next;
  315.     if (a->sub_prev) a->sub_prev->sub_next = a->sub_next;
  316.     if (a->sub_next) a->sub_next->sub_prev = a->sub_prev;
  317.   }
  318.   
  319.   free_blocks (a->first);
  320.   unblock_alarms();
  321. }
  322.  
  323. long bytes_in_pool (pool *p) { return bytes_in_block_list (p->first); }
  324. long bytes_in_free_blocks () { return bytes_in_block_list (block_freelist); }
  325.  
  326. /*****************************************************************
  327.  *
  328.  * Allocating stuff...
  329.  */
  330.  
  331.  
  332. void *palloc (struct pool *a, int reqsize)
  333. {
  334.   /* Round up requested size to an even number of alignment units (core clicks)
  335.    */
  336.   
  337.   int nclicks = 1 + ((reqsize - 1) / CLICK_SZ);
  338.   int size = nclicks * CLICK_SZ;
  339.   
  340.   /* First, see if we have space in the block most recently
  341.    * allocated to this pool
  342.    */
  343.   
  344.   union block_hdr *blok = a->last; 
  345.   char *first_avail = blok->h.first_avail;
  346.   char *new_first_avail;
  347.  
  348.   if (size <= 0) size = 1;
  349.   new_first_avail = first_avail + size;
  350.   
  351.   if (new_first_avail <= blok->h.endp) {
  352.     blok->h.first_avail = new_first_avail;
  353.     return (void *)first_avail;
  354.   }
  355.  
  356.   /* Nope --- get a new one that's guaranteed to be big enough */
  357.   
  358.   block_alarms();
  359.   blok = new_block (size);
  360.   a->last->h.next = blok;
  361.   a->last = blok;
  362.   unblock_alarms();
  363.  
  364.   first_avail = blok->h.first_avail;
  365.   blok->h.first_avail += size;
  366.  
  367.   return (void *)first_avail;
  368. }
  369.  
  370. void *pcalloc(struct pool *a, int size)
  371. {
  372.   void *res = palloc (a, size);
  373.   memset (res, '\0', size);
  374.   return res;
  375. }
  376.  
  377. char *pstrdup(struct pool *a, const char *s)
  378. {
  379.   char *res;
  380.   if (s == NULL) return NULL;
  381.   res = palloc (a, strlen(s) + 1);
  382.   strcpy (res, s);
  383.   return res;
  384. }
  385.  
  386. char *pstrcat(pool *a, ...)
  387. {
  388.   char *cp, *argp, *res;
  389.   
  390.   /* Pass one --- find length of required string */
  391.   
  392.   int len = 0;
  393.   va_list adummy;
  394.   
  395.   va_start (adummy, a);
  396.  
  397.   while ((cp = va_arg (adummy, char *)) != NULL) 
  398.     len += strlen(cp);
  399.  
  400.   va_end (adummy);
  401.  
  402.   /* Allocate the required string */
  403.  
  404.   res = (char *)palloc(a, len + 1);
  405.   cp = res;
  406.  
  407.   /* Pass two --- copy the argument strings into the result space */
  408.  
  409.   va_start (adummy, a);
  410.   
  411.   while ((argp = va_arg (adummy, char *)) != NULL) {
  412.     strcpy (cp, argp);
  413.     cp += strlen(argp);
  414.   }
  415.  
  416.   va_end (adummy);
  417.  
  418.   /* Return the result string */
  419.  
  420.   return res;
  421. }
  422.  
  423.  
  424. /*****************************************************************
  425.  *
  426.  * The 'array' functions...
  427.  */
  428.  
  429. array_header *make_array (pool *p, int nelts, int elt_size)
  430. {
  431.   array_header *res = (array_header *)palloc(p, sizeof(array_header));
  432.  
  433.   if (nelts < 1) nelts = 1;    /* Assure sanity if someone asks for
  434.                  * array of zero elts.
  435.                  */
  436.   
  437.   res->elts = pcalloc (p, nelts * elt_size);
  438.   
  439.   res->pool = p;
  440.   res->elt_size = elt_size;
  441.   res->nelts = 0;        /* No active elements yet... */
  442.   res->nalloc = nelts;        /* ...but this many allocated */
  443.  
  444.   return res;
  445. }
  446.  
  447. void *push_array (array_header *arr)
  448. {
  449.   if (arr->nelts == arr->nalloc) {
  450.     char *new_data = pcalloc (arr->pool, arr->nalloc * arr->elt_size * 2);
  451.  
  452.     memcpy (new_data, arr->elts, arr->nalloc * arr->elt_size);
  453.     arr->elts = new_data;
  454.     arr->nalloc *= 2;
  455.   }
  456.  
  457.   ++arr->nelts;
  458.   return arr->elts + (arr->elt_size * (arr->nelts - 1));
  459. }
  460.  
  461. void array_cat (array_header *dst, array_header *src)
  462. {
  463.   int elt_size = dst->elt_size;
  464.   
  465.   if (dst->nelts + src->nelts > dst->nalloc) {
  466.     int new_size = dst->nalloc * 2;
  467.     char *new_data;
  468.  
  469.     if (new_size == 0) ++new_size;
  470.     
  471.     while (dst->nelts + src->nelts > new_size)
  472.       new_size *= 2;
  473.  
  474.     new_data = pcalloc (dst->pool, elt_size * new_size);
  475.     memcpy (new_data, dst->elts, dst->nalloc * elt_size);
  476.     
  477.     dst->elts = new_data;
  478.     dst->nalloc = new_size;
  479.   }
  480.  
  481.   memcpy (dst->elts + dst->nelts * elt_size, src->elts, elt_size * src->nelts);
  482.   dst->nelts += src->nelts;
  483. }
  484.  
  485. array_header *copy_array (pool *p, array_header *arr)
  486. {
  487.   array_header *res = make_array (p, arr->nalloc, arr->elt_size);
  488.  
  489.   memcpy (res->elts, arr->elts, arr->elt_size * arr->nelts);
  490.   res->nelts = arr->nelts;
  491.   return res;
  492. }
  493.  
  494. /* This cute function copies the array header *only*, but arranges
  495.  * for the data section to be copied on the first push or arraycat.
  496.  * It's useful when the elements of the array being copied are
  497.  * read only, but new stuff *might* get added on the end; we have the
  498.  * overhead of the full copy only where it is really needed.
  499.  */
  500.  
  501. array_header *copy_array_hdr (pool *p, array_header *arr)
  502. {
  503.   array_header *res = (array_header *)palloc(p, sizeof(array_header));
  504.  
  505.   res->elts = arr->elts;
  506.   
  507.   res->pool = p;
  508.   res->elt_size = arr->elt_size;
  509.   res->nelts = arr->nelts;
  510.   res->nalloc = arr->nelts;    /* Force overflow on push */
  511.  
  512.   return res;
  513. }
  514.  
  515. /* The above is used here to avoid consing multiple new array bodies... */
  516.  
  517. array_header *append_arrays (pool *p,
  518.                  array_header *first, array_header *second)
  519. {
  520.   array_header *res = copy_array_hdr (p, first);
  521.  
  522.   array_cat (res, second);
  523.   return res;
  524. }
  525.  
  526.  
  527. /*****************************************************************
  528.  *
  529.  * The "table" functions.
  530.  */
  531.  
  532. table *make_table (pool *p, int nelts) {
  533.     return make_array (p, nelts, sizeof (table_entry));
  534. }
  535.  
  536. table *copy_table (pool *p, table *t) {
  537.     return copy_array (p, t);
  538. }
  539.  
  540. array_header *table_elts (table *t) { return t; }
  541.  
  542. char *table_get (table *t, char *key)
  543. {
  544.     table_entry *elts = (table_entry *)t->elts;
  545.     int i;
  546.  
  547.     if (key == NULL) return NULL;
  548.     
  549.     for (i = 0; i < t->nelts; ++i)
  550.         if (!strcasecmp (elts[i].key, key))
  551.         return elts[i].val;
  552.  
  553.     return NULL;
  554. }
  555.  
  556. void table_set (table *t, char *key, char *val)
  557. {
  558.     table_entry *elts = (table_entry *)t->elts;
  559.     int i;
  560.  
  561.     for (i = 0; i < t->nelts; ++i)
  562.         if (!strcasecmp (elts[i].key, key)) {
  563.         elts[i].val = pstrdup (t->pool, val);
  564.         return;
  565.     }
  566.  
  567.     elts = (table_entry *)push_array(t);
  568.     elts->key = pstrdup (t->pool, key);
  569.     elts->val = pstrdup (t->pool, val);
  570. }
  571.  
  572. void table_merge (table *t, char *key, char *val)
  573. {
  574.     table_entry *elts = (table_entry *)t->elts;
  575.     int i;
  576.  
  577.     for (i = 0; i < t->nelts; ++i)
  578.         if (!strcasecmp (elts[i].key, key)) {
  579.         elts[i].val = pstrcat (t->pool, elts[i].val, ", ", val, NULL);
  580.         return;
  581.     }
  582.  
  583.     elts = (table_entry *)push_array(t);
  584.     elts->key = pstrdup (t->pool, key);
  585.     elts->val = pstrdup (t->pool, val);
  586. }
  587.  
  588. table* overlay_tables (pool *p, table *overlay, table *base)
  589. {
  590.     return append_arrays (p, overlay, base);
  591. }
  592.  
  593. /*****************************************************************
  594.  *
  595.  * Managing generic cleanups.  
  596.  */
  597.  
  598. struct cleanup {
  599.   void *data;
  600.   void (*plain_cleanup)(void *);
  601.   void (*child_cleanup)(void *);
  602.   struct cleanup *next;
  603. };
  604.  
  605. void register_cleanup (pool *p, void *data, void (*plain_cleanup)(void *),
  606.                void (*child_cleanup)(void *))
  607. {
  608.   struct cleanup *c = (struct cleanup *)palloc(p, sizeof (struct cleanup));
  609.   c->data = data;
  610.   c->plain_cleanup = plain_cleanup;
  611.   c->child_cleanup = child_cleanup;
  612.   c->next = p->cleanups;
  613.   p->cleanups = c;
  614. }
  615.  
  616. void kill_cleanup (pool *p, void *data, void (*cleanup)(void *))
  617. {
  618.   struct cleanup *c = p->cleanups;
  619.   struct cleanup **lastp = &p->cleanups;
  620.     
  621.   while (c) {
  622.     if (c->data == data && c->plain_cleanup == cleanup) {
  623.       *lastp = c->next;
  624.       break;
  625.     }
  626.  
  627.     lastp = &c->next;
  628.     c = c->next;
  629.   }
  630. }
  631.  
  632. void run_cleanup (pool *p, void *data, void (*cleanup)(void *))
  633. {
  634.   block_alarms();        /* Run cleanup only once! */
  635.   (*cleanup)(data);
  636.   kill_cleanup (p, data, cleanup);
  637.   unblock_alarms();
  638. }
  639.  
  640. static void run_cleanups (struct cleanup *c)
  641. {
  642.   while (c) {
  643.     (*c->plain_cleanup)(c->data);
  644.     c = c->next;
  645.   }
  646. }
  647.  
  648. static void run_child_cleanups (struct cleanup *c)
  649. {
  650.   while (c) {
  651.     (*c->child_cleanup)(c->data);
  652.     c = c->next;
  653.   }
  654. }
  655.  
  656. static void cleanup_pool_for_exec (pool *p)
  657. {
  658.   run_child_cleanups (p->cleanups);
  659.   p->cleanups = NULL;
  660.  
  661.   for (p = p->sub_pools; p; p = p->sub_next)
  662.     cleanup_pool_for_exec (p);
  663. }
  664.  
  665. void cleanup_for_exec()
  666. {
  667.   block_alarms();
  668.   cleanup_pool_for_exec (permanent_pool);
  669.   unblock_alarms();
  670. }
  671.  
  672. /*****************************************************************
  673.  *
  674.  * Files and file descriptors; these are just an application of the
  675.  * generic cleanup interface.
  676.  */
  677.  
  678. static void fd_cleanup (void *fdv) { close ((int)fdv); }
  679.  
  680. void note_cleanups_for_fd (pool *p, int fd) {
  681.   register_cleanup (p, (void *)fd, fd_cleanup, fd_cleanup);
  682. }
  683.  
  684. int popenf(struct pool *a, char *name, int flg, int mode)
  685. {
  686.   int fd;
  687.  
  688.   block_alarms();
  689.   fd = open(name, flg, mode);
  690.   if (fd >= 0) note_cleanups_for_fd (a, fd);
  691.   unblock_alarms();
  692.   return fd;
  693. }
  694.  
  695. int pclosef(struct pool *a, int fd)
  696. {
  697.   int res;
  698.   
  699.   block_alarms();
  700.   res = close(fd);
  701.   kill_cleanup(a, (void *)fd, fd_cleanup);
  702.   unblock_alarms();
  703.   return res;
  704. }
  705.  
  706. /* Note that we have separate plain_ and child_ cleanups for FILE *s,
  707.  * since fclose() would flush I/O buffers, which is extremely undesirable;
  708.  * we just close the descriptor.
  709.  */
  710.  
  711. static void file_cleanup (void *fpv) { fclose ((FILE *)fpv); }
  712. static void file_child_cleanup (void *fpv) { close (fileno ((FILE *)fpv)); }
  713.  
  714. void note_cleanups_for_file (struct pool *p, FILE *fp) {
  715.   register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup);
  716. }
  717.  
  718. FILE *pfopen(struct pool *a, char *name, char *mode)
  719. {
  720.   FILE *fd;
  721.  
  722.   block_alarms();
  723.   fd = fopen(name, mode);
  724.   if (fd != NULL) note_cleanups_for_file (a, fd);
  725.   unblock_alarms();
  726.   return fd;
  727. }
  728.  
  729. FILE *pfdopen(struct pool *a,int fd,char *mode)
  730. {
  731.   FILE *f;
  732.  
  733.   block_alarms();
  734.   f=fdopen(fd,mode);
  735.   if(f != NULL)
  736.     note_cleanups_for_file(a,f);
  737.   unblock_alarms();
  738.   return f;
  739. }
  740.  
  741.  
  742. int pfclose(struct pool *a, FILE *fd)
  743. {
  744.   int res;
  745.   
  746.   block_alarms();
  747.   res = fclose(fd);
  748.   kill_cleanup(a, (void *)fd, file_cleanup);
  749.   unblock_alarms();
  750.   return res;
  751. }
  752.  
  753. /*****************************************************************
  754.  *
  755.  * More grotty system stuff... subprocesses.  Frump.  These don't use
  756.  * the generic cleanup interface because I don't want multiple
  757.  * subprocesses to result in multiple three-second pauses; the
  758.  * subprocesses have to be "freed" all at once.  If someone comes
  759.  * along with another resource they want to allocate which has the
  760.  * same property, we might want to fold support for that into the
  761.  * generic interface, but for now, it's a special case
  762.  */
  763.  
  764. struct process_chain {
  765.   pid_t pid;
  766.   enum kill_conditions kill_how;
  767.   struct process_chain *next;
  768. };
  769.  
  770. void note_subprocess (pool *a, int pid, enum kill_conditions how)
  771. {
  772.   struct process_chain *new =
  773.     (struct process_chain *)palloc(a, sizeof(struct process_chain));
  774.  
  775.   new->pid = pid;
  776.   new->kill_how = how;
  777.   new->next = a->subprocesses;
  778.   a->subprocesses = new;
  779. }
  780.  
  781. int spawn_child (pool *p, void (*func)(void *), void *data,
  782.          enum kill_conditions kill_how,
  783.          FILE **pipe_in, FILE **pipe_out)
  784. {
  785.   int pid;
  786.   int in_fds[2];
  787.   int out_fds[2];
  788.  
  789.   block_alarms();
  790.   
  791.   if (pipe_in && pipe (in_fds) < 0)
  792.   {
  793.       unblock_alarms();
  794.       return 0;
  795.   }
  796.   
  797.   if (pipe_out && pipe (out_fds) < 0) {
  798.     if (pipe_in) {
  799.       close (in_fds[0]); close (in_fds[1]);
  800.     }
  801.     unblock_alarms();
  802.     return 0;
  803.   }
  804.  
  805.   if ((pid = fork()) < 0) {
  806.     if (pipe_in) {
  807.       close (in_fds[0]); close (in_fds[1]);
  808.     }
  809.     if (pipe_out) {
  810.       close (out_fds[0]); close (out_fds[1]);
  811.     }
  812.     unblock_alarms();
  813.     return 0;
  814.   }
  815.  
  816.   if (!pid) {
  817.     /* Child process */
  818.     
  819.     if (pipe_out) {
  820.       close (out_fds[0]);
  821.       dup2 (out_fds[1], STDOUT_FILENO);
  822.       close (out_fds[1]);
  823.     }
  824.  
  825.     if (pipe_in) {
  826.       close (in_fds[1]);
  827.       dup2 (in_fds[0], STDIN_FILENO);
  828.       close (in_fds[0]);
  829.     }
  830.  
  831.     /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
  832.     signal (SIGCHLD, SIG_DFL);    /* Was that it? */
  833.     
  834.     func (data);
  835.     exit (0);            /* Should never get here... */
  836.   }
  837.  
  838.   /* Parent process */
  839.  
  840.   note_subprocess (p, pid, kill_how);
  841.   
  842.   if (pipe_out) {
  843.     close (out_fds[1]);
  844.     *pipe_out = fdopen (out_fds[0], "r");
  845.     
  846.     if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
  847.   }
  848.  
  849.   if (pipe_in) {
  850.     close (in_fds[0]);
  851.     *pipe_in = fdopen (in_fds[1], "w");
  852.     
  853.     if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
  854.   }
  855.  
  856.   unblock_alarms();
  857.   return pid;
  858. }
  859.  
  860. static void free_proc_chain (struct process_chain *procs)
  861. {
  862.   /* Dispose of the subprocesses we've spawned off in the course of
  863.    * whatever it was we're cleaning up now.  This may involve killing
  864.    * some of them off...
  865.    */
  866.  
  867.   struct process_chain *p;
  868.   int need_timeout = 0;
  869.   int status;
  870.  
  871.   if (procs == NULL) return;    /* No work.  Whew! */
  872.  
  873.   /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL
  874.    * dance with any of the processes we're cleaning up.  If we've got
  875.    * any kill-on-sight subprocesses, ditch them now as well, so they
  876.    * don't waste any more cycles doing whatever it is that they shouldn't
  877.    * be doing anymore.
  878.    */
  879.  
  880. #ifndef NEED_WAITPID
  881.   /* Pick up all defunct processes */
  882.   for (p = procs; p; p = p->next) {
  883.     if (waitpid (p->pid, (int *) 0, WNOHANG) > 0) {
  884.       p->kill_how = kill_never;
  885.     }
  886.   }
  887. #endif
  888.  
  889.   for (p = procs; p; p = p->next) {
  890.     if (p->kill_how == kill_after_timeout) {
  891.       /* Subprocess may be dead already.  Only need the timeout if not. */
  892.       if (kill (p->pid, SIGTERM) != -1)
  893.     need_timeout = 1;
  894.     } else if (p->kill_how == kill_always) {
  895.       kill (p->pid, SIGKILL);
  896.     }
  897.   }
  898.  
  899.   /* Sleep only if we have to... */
  900.  
  901.   if (need_timeout) sleep (3);
  902.  
  903.   /* OK, the scripts we just timed out for have had a chance to clean up
  904.    * --- now, just get rid of them, and also clean up the system accounting
  905.    * goop...
  906.    */
  907.  
  908.   for (p = procs; p; p = p->next){
  909.     
  910.     if (p->kill_how == kill_after_timeout) 
  911.       kill (p->pid, SIGKILL);
  912.  
  913.     if (p->kill_how != kill_never)
  914.       waitpid (p->pid, &status, 0);
  915.   }
  916. }
  917.